/*
Copyright (c) 2020-2022, Huawei Technologies Co., Ltd
 All rights reserved.

Redistribution and use in source and binary forms, with or without modification, are permitted provided that the
following conditions are met:

1. Redistributions of source code must retain the above copyright notice, this list of conditions and the
following disclaimer.

2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the
following disclaimer in the documentation and/or other materials provided with the distribution.

3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote
products derived from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

* Description: 2D图表可视化插件数据显示类
*/
#include "st_space_display.h"

#include <qwt_legend_label.h>
#include "common/utils.h"
#include <QScrollBar>

namespace chart_plugin {
using rviz::ColorProperty;
using rviz::Config;
using rviz::EnumProperty;
using rviz::IntProperty;
using rviz::FloatProperty;
using rviz::Property;
using rviz::StatusProperty;
using rviz::StringProperty;

using std::make_unique;
using std::map;
using std::unique_ptr;

const int32_t FIRST_CURVE_COLOR_R = 0xF5;
const int32_t FIRST_CURVE_COLOR_G = 0xF2;
const int32_t FIRST_CURVE_COLOR_B = 0x2E;
const qreal FIRST_CURVE_COLOR_ALPHA = 0x01;

const int32_t OPTIMIZED_CURVE_COLOR_R = 0xFF;
const int32_t OPTIMIZED_CURVE_COLOR_G = 0x3B;
const int32_t OPTIMIZED_CURVE_COLOR_B = 0x3B;
const qreal OPTIMIZED_CURVE_COLOR_ALPHA = 0x01;

const int32_t SPEED_LIMIT_CURVE_COLOR_R = 0x00;
const int32_t SPEED_LIMIT_CURVE_COLOR_G = 0xFF;
const int32_t SPEED_LIMIT_CURVE_COLOR_B = 0xC1;
const qreal SPEED_LIMIT_CURVE_COLOR_ALPHA = 0x01;

const int32_t COLOR_ALPHA_MIN = 0x00;
const int32_t COLOR_ALPHA_MAX = 0x01;

const int32_t COLOR_MIN = 0x00;
const int32_t COLOR_MAX = 0xFF;

const qreal CURVE_PEN_WIDTH = 0x02;
const qreal OBJECT_CURVE_PEN_WIDTH = 0x01;
const qreal CURVE_SYMBOL_PEN_WIDTH = 0x01;
const int32_t CURVE_SYMBOL_SIZE_WIDTH = 0x02;
const int32_t CURVE_SYMBOL_SIZE_HRIGHT = 0x02;

const QString LEGEND_STYLE_SHEET = "color: rgb(255, 255, 255);";

const QString QWTPLOT_TITLE_FONT_FAMILY = "楷体";
const int32_t QWTPLOT_TITLE_FONT_POINT_SIZE = 0x10;

const int32_t QWTPLOT_GRID_COLOR_R = 0x00;
const int32_t QWTPLOT_GRID_COLOR_G = 0x73;
const int32_t QWTPLOT_GRID_COLOR_B = 0x81;
const qreal QWTPLOT_GRID_PEN_WIDTH = 0x1;

const double QWTPLOT_AXIS_XBOTTOM_MIN = 0x00;
const double QWTPLOT_AXIS_XBOTTOM_MAX = 0x0A;

const double QWTPLOT_AXIS_YLEFT_MIN = 0x00;
const double QWTPLOT_AXIS_YLEFT_MAX = 0x96;

const int32_t OBJECT_TYPE_KEY_KEY_SIZE = 3;
const int32_t OBJECT_VALUE_KEY_VALUE_SIZE = 2;

enum CurveType {
    FIRST_ST_TRACK_CURVE = 0,
    OPTIMIZED_ST_TRACK_CURVE,
    ST_SPEED_LIMIT_CURVE,
};

void StSpaceDisplay::InitTitleProperty()
{
    qwtPlotTitleProperty = new
        StringProperty("Chart Title", "ST图", "Chart title name property.", this, SLOT(UpdateChartTitle()), this);
    qwtPlotTitleProperty->setReadOnly(true);

    xAxisTitleProperty = new
        StringProperty("X Axis Title", "T [s]", "X Axis title name property.", this, SLOT(UpdateXAxisTitle()), this);
    xAxisTitleProperty->setReadOnly(true);

    yAxisTitleProperty = new
        StringProperty("Y Axis Title", "S [m]", "Y Axis title name property.", this, SLOT(UpdateYAxisTitle()), this);
    yAxisTitleProperty->setReadOnly(true);
}

void StSpaceDisplay::InitFirstCurveProperty(EnumProperty &curveProperty)
{
    firstCurveTitleProperty = new StringProperty("First Title", "First Track",
        "First curve title property setting.", &curveProperty, SLOT(UpdateCurveTitle()), this);

    firstCurveColorProperty = new
        ColorProperty("First Color", QColor(FIRST_CURVE_COLOR_R, FIRST_CURVE_COLOR_G, FIRST_CURVE_COLOR_B),
        "Color of the first Curve.", &curveProperty, SLOT(UpdateCurveColorAndAlpha()), this);

    firstCurveAlphaProperty = new FloatProperty("First Alpha", FIRST_CURVE_COLOR_ALPHA,
        "First curve alpha property setting.", &curveProperty, SLOT(UpdateCurveColorAndAlpha()), this);

    firstCurveAlphaProperty->setMin(COLOR_ALPHA_MIN);
    firstCurveAlphaProperty->setMax(COLOR_ALPHA_MAX);
}

void StSpaceDisplay::InitSecondCurveProperty(EnumProperty &curveProperty)
{
    optimizedCurveTitleProperty = new StringProperty("Optimized Title", "Optimized Track",
        "Optimized curve title property setting.", &curveProperty, SLOT(UpdateCurveTitle()), this);

    optimizedCurveColorProperty = new ColorProperty("Optimized Color",
        QColor(OPTIMIZED_CURVE_COLOR_R, OPTIMIZED_CURVE_COLOR_G, OPTIMIZED_CURVE_COLOR_B),
        "Color of the Optimized St Track Curve.", &curveProperty, SLOT(UpdateCurveColorAndAlpha()), this);

    optimizedCurveAlphaProperty = new FloatProperty("Optimized Alpha", OPTIMIZED_CURVE_COLOR_ALPHA,
        "Optimized St Track curve alpha property setting.", &curveProperty, SLOT(UpdateCurveColorAndAlpha()), this);

    optimizedCurveAlphaProperty->setMin(COLOR_ALPHA_MIN);
    optimizedCurveAlphaProperty->setMax(COLOR_ALPHA_MAX);
}

void StSpaceDisplay::InitSpeedLimitCurveProperty(EnumProperty &curveProperty)
{
    speedLimitCurveTitleProperty = new StringProperty("Speed Title", "St Speed Limit",
        "St Speed Limit curve title property setting.", &curveProperty, SLOT(UpdateCurveTitle()), this);

    speedLimitCurveColorProperty = new ColorProperty("Speed Color",
        QColor(SPEED_LIMIT_CURVE_COLOR_R, SPEED_LIMIT_CURVE_COLOR_G, SPEED_LIMIT_CURVE_COLOR_B),
        "Color of the St Speed Limit Curve.", &curveProperty, SLOT(UpdateCurveColorAndAlpha()), this);

    speedLimitCurveAlphaProperty = new FloatProperty("Speed Alpha", SPEED_LIMIT_CURVE_COLOR_ALPHA,
        "St Speed Limit curve alpha property setting.", &curveProperty, SLOT(UpdateCurveColorAndAlpha()), this);

    speedLimitCurveAlphaProperty->setMin(COLOR_ALPHA_MIN);
    speedLimitCurveAlphaProperty->setMax(COLOR_ALPHA_MAX);
}


void StSpaceDisplay::InitCurveProperty()
{
    curveProperty = new 
        EnumProperty("Curve", "First St Track", "Curve type setting selection.", this, SLOT(UpdateCurveChoice()));
    curveProperty->addOption("First St Track", FIRST_ST_TRACK_CURVE);
    curveProperty->addOption("Optimized St Track", OPTIMIZED_ST_TRACK_CURVE);
    curveProperty->addOption("St Speed Limit", ST_SPEED_LIMIT_CURVE);

    InitFirstCurveProperty(*curveProperty);
    InitSecondCurveProperty(*curveProperty);
    InitSpeedLimitCurveProperty(*curveProperty);
}

void StSpaceDisplay::InitProperty()
{
    InitTitleProperty();
    InitCurveProperty();
}

void StSpaceDisplay::UpdateCurveColorAndAlpha()
{
    QColor colorFirstCurve = firstCurveColorProperty->getColor();
    QColor colorOptimizedCurve = optimizedCurveColorProperty->getColor();
    QColor colorSpeedLimitCurve = speedLimitCurveColorProperty->getColor();

    qreal alphaFirstCurve = firstCurveAlphaProperty->getFloat();
    qreal alphaOptimizedCurve = optimizedCurveAlphaProperty->getFloat();
    qreal alphaSpeedLimitCurve = speedLimitCurveAlphaProperty->getFloat();

    colorFirstCurve.setAlpha(alphaFirstCurve * 0xFF);
    colorOptimizedCurve.setAlpha(alphaOptimizedCurve * 0xFF);
    colorSpeedLimitCurve.setAlpha(alphaSpeedLimitCurve * 0xFF);

    firstStTrackCurve.setPen(colorFirstCurve, CURVE_PEN_WIDTH);
    optimizedStTrackCurve.setPen(colorOptimizedCurve, CURVE_PEN_WIDTH);
    stSpeedLimitCurve.setPen(colorSpeedLimitCurve, CURVE_PEN_WIDTH);

    QwtSymbol *firstSymbol = new QwtSymbol(QwtSymbol::Ellipse, QBrush(colorFirstCurve),
        QPen(colorFirstCurve, CURVE_SYMBOL_PEN_WIDTH), QSize(CURVE_SYMBOL_SIZE_WIDTH, CURVE_SYMBOL_SIZE_HRIGHT));

    firstStTrackCurve.setSymbol(firstSymbol);

    QwtSymbol *optimizedSymbol = new QwtSymbol(QwtSymbol::Ellipse, QBrush(colorOptimizedCurve),
        QPen(colorOptimizedCurve, CURVE_SYMBOL_PEN_WIDTH), QSize(CURVE_SYMBOL_SIZE_WIDTH, CURVE_SYMBOL_SIZE_HRIGHT));

    optimizedStTrackCurve.setSymbol(optimizedSymbol);

    QwtSymbol *stSpeedSymbol = new QwtSymbol(QwtSymbol::Ellipse, QBrush(colorSpeedLimitCurve),
        QPen(colorSpeedLimitCurve, CURVE_SYMBOL_PEN_WIDTH), QSize(CURVE_SYMBOL_SIZE_WIDTH, CURVE_SYMBOL_SIZE_HRIGHT));

    stSpeedLimitCurve.setSymbol(stSpeedSymbol);
    ui.qwtPlot->replot();
}

void StSpaceDisplay::UpdateCurveTitle()
{
    firstStTrackCurve.setTitle(firstCurveTitleProperty->getString());
    optimizedStTrackCurve.setTitle(optimizedCurveTitleProperty->getString());
    stSpeedLimitCurve.setTitle(speedLimitCurveTitleProperty->getString());

    ui.qwtPlot->replot();
}

void StSpaceDisplay::UpdateObjectCurveTitle()
{
    int32_t type = objectsTypeProperty->getOptionInt();

    const QMap<QString, int32_t> objectsIdTypeMap = stSpaceData.GetObjectsIdTypeMap();
    for (auto iter = objectsIdTypeMap.begin(); iter != objectsIdTypeMap.end(); iter++) {
        auto objectIdLegendIter = stObjectsCurveMap.find(iter.key());
        if ((objectIdLegendIter != stObjectsCurveMap.end()) && (iter.value() == type) &&
            objectsTypeTitleProperty.contains(type) && objectsTypeTitleProperty[type] != nullptr) {
            ((*objectIdLegendIter).second)
                ->setTitle(QString("%1: %2").arg(objectsTypeTitleProperty[type]->getString()).arg(iter.key()));
        }
    }
    ui.qwtPlot->replot();
}

void StSpaceDisplay::UpdateFirstCurveChoice()
{
    bool useFirstStTrackCurve = false;

    if (curveProperty->getOptionInt() == FIRST_ST_TRACK_CURVE) {
        useFirstStTrackCurve = true;
    }

    firstCurveTitleProperty->setHidden(!useFirstStTrackCurve);
    firstCurveColorProperty->setHidden(!useFirstStTrackCurve);
    firstCurveAlphaProperty->setHidden(!useFirstStTrackCurve);
}

void StSpaceDisplay::UpdateOptimizedCurveChoice()
{
    bool useOptimizedStTrackCurve = false;

    if (curveProperty->getOptionInt() == OPTIMIZED_ST_TRACK_CURVE) {
        useOptimizedStTrackCurve = true;
    }

    optimizedCurveTitleProperty->setHidden(!useOptimizedStTrackCurve);
    optimizedCurveColorProperty->setHidden(!useOptimizedStTrackCurve);
    optimizedCurveAlphaProperty->setHidden(!useOptimizedStTrackCurve);
}

void StSpaceDisplay::UpdateStSpeedCurveChoice()
{
    bool useStSpeedLimitCurve = false;

    if (curveProperty->getOptionInt() == ST_SPEED_LIMIT_CURVE) {
        useStSpeedLimitCurve = true;
    }

    speedLimitCurveTitleProperty->setHidden(!useStSpeedLimitCurve);
    speedLimitCurveColorProperty->setHidden(!useStSpeedLimitCurve);
    speedLimitCurveAlphaProperty->setHidden(!useStSpeedLimitCurve);
}

void StSpaceDisplay::UpdateCurveChoice()
{
    UpdateFirstCurveChoice();
    UpdateOptimizedCurveChoice();
    UpdateStSpeedCurveChoice();
}

void StSpaceDisplay::UpdateChartTitle()
{
    QString title = qwtPlotTitleProperty->getString();
    ui.qwtPlot->setTitle(title);
}

void StSpaceDisplay::UpdateXAxisTitle()
{
    QString title = xAxisTitleProperty->getString();
    ui.qwtPlot->setAxisTitle(QwtPlot::xBottom, title);
}

void StSpaceDisplay::UpdateYAxisTitle()
{
    QString title = yAxisTitleProperty->getString();
    ui.qwtPlot->setAxisTitle(QwtPlot::yLeft, title);
}

void StSpaceDisplay::ShowCurve(QwtPlotItem *item, bool isVisible)
{
    if (item == nullptr) {
        return;
    }

    item->setVisible(isVisible);
    QwtLegend *legend = qobject_cast<QwtLegend *>(ui.qwtPlot->legend());
    QList<QWidget *> legendWidgets = legend->legendWidgets(ui.qwtPlot->itemToInfo(item));
    for (int32_t i = 0; i < legendWidgets.size(); i++) {
        QwtLegendLabel *legendLabel = qobject_cast<QwtLegendLabel *>(legendWidgets.at(i));
        if (legendLabel != nullptr) {
            legendLabel->setChecked(isVisible);
        }
    }
}

void StSpaceDisplay::LegendChecked(const QVariant &itemInfo, bool isVisible)
{
    QwtPlotItem *plotItem = ui.qwtPlot->infoToItem(itemInfo);

    if (plotItem != nullptr) {
        plotItem->setVisible(isVisible);
        for (auto iter = stObjectsCurveMap.begin(); iter != stObjectsCurveMap.end(); iter++) {
            if (plotItem == iter->second.get()) {
                stSpaceData.SetObjectVisible(iter->first, isVisible);
            }
        }
    }

    ui.qwtPlot->replot();
}

void StSpaceDisplay::InitQwtPlotStyle()
{
    QwtText title;
    // 设置标题名
    title.setText(qwtPlotTitleProperty->getString());

    // 设置字体为楷体，大小为 0x10
    QFont font;
    font.setFamily(QWTPLOT_TITLE_FONT_FAMILY);
    font.setPointSize(QWTPLOT_TITLE_FONT_POINT_SIZE);

    // 设置粗体
    font.setBold(true);

    title.setFont(font);
    ui.qwtPlot->setTitle(title);

    QwtLegend *legend = new QwtLegend();

    if (legend != nullptr) {
        legend->setStyleSheet(LEGEND_STYLE_SHEET);

        // 图例可以选择,Checkable
        legend->setDefaultItemMode(QwtLegendData::Checkable);

        // 设置图例的位置
        ui.qwtPlot->insertLegend(legend, QwtPlot::BottomLegend);

        // 连接槽，处理选择事件
        connect(legend, SIGNAL(checked(const QVariant &, bool, int)), SLOT(LegendChecked(const QVariant &, bool)));
        legend->horizontalScrollBar()->setPalette(QPalette());
        legend->verticalScrollBar()->setPalette(QPalette());
    }

    // 绘制网格
    // 设置画笔
    grid.setPen(QColor(QWTPLOT_GRID_COLOR_R, QWTPLOT_GRID_COLOR_G, QWTPLOT_GRID_COLOR_B), QWTPLOT_GRID_PEN_WIDTH);
    grid.attach(ui.qwtPlot);

    // 设置坐标轴 X的标题
    ui.qwtPlot->setAxisTitle(QwtPlot::xBottom, xAxisTitleProperty->getString());

    // X轴的范围
    ui.qwtPlot->setAxisScale(QwtPlot::xBottom, QWTPLOT_AXIS_XBOTTOM_MIN, QWTPLOT_AXIS_XBOTTOM_MAX);

    // Y轴名字
    ui.qwtPlot->setAxisTitle(QwtPlot::yLeft, yAxisTitleProperty->getString());

    // Y轴范围
    ui.qwtPlot->setAxisScale(QwtPlot::yLeft, QWTPLOT_AXIS_YLEFT_MIN, QWTPLOT_AXIS_YLEFT_MAX);
    ui.qwtPlot->setAxisAutoScale(QwtPlot::xBottom, true);
    ui.qwtPlot->setAxisAutoScale(QwtPlot::yLeft, true);
}

void StSpaceDisplay::InitCurveStyle(QwtPlotCurve &curve, StringProperty &title, const ColorProperty &color,
    const FloatProperty &alpha)
{
    // 图例名字
    curve.setTitle(title.getString());

    QColor colorCurve = color.getColor();
    qreal alphaCurve = alpha.getFloat();
    colorCurve.setAlpha(alphaCurve * 0xFF);
    // 设置画笔
    curve.setPen(colorCurve, CURVE_PEN_WIDTH);

    // 设置反锯齿
    curve.setRenderHint(QwtPlotItem::RenderAntialiased, true);

    // 显示图例的标志，这里显示线的颜色
    // 设置样本点的颜色、大小
    QwtSymbol *symbol = new QwtSymbol(QwtSymbol::Ellipse, QBrush(colorCurve),
        QPen(colorCurve, CURVE_SYMBOL_PEN_WIDTH), QSize(CURVE_SYMBOL_SIZE_WIDTH, CURVE_SYMBOL_SIZE_HRIGHT));

    // 添加样本点形状
    curve.setSymbol(symbol);
    curve.setLegendAttribute(curve.LegendShowSymbol);
}

void StSpaceDisplay::onInitialize()
{
    InitProperty();
    ChartDisplayClass::onInitialize();
    ui.SetupUi(widget);

    InitQwtPlotStyle();
    InitCurveStyle(firstStTrackCurve, *firstCurveTitleProperty, *firstCurveColorProperty, *firstCurveAlphaProperty);
    // 添加到控件上
    firstStTrackCurve.attach(ui.qwtPlot);
    ShowCurve(&firstStTrackCurve, true);

    InitCurveStyle(optimizedStTrackCurve, *optimizedCurveTitleProperty, *optimizedCurveColorProperty,
        *optimizedCurveAlphaProperty);
    // 添加到控件上
    optimizedStTrackCurve.attach(ui.qwtPlot);
    ShowCurve(&optimizedStTrackCurve, true);

    InitCurveStyle(stSpeedLimitCurve, *speedLimitCurveTitleProperty, *speedLimitCurveColorProperty,
        *speedLimitCurveAlphaProperty);
    // 添加到控件上
    stSpeedLimitCurve.attach(ui.qwtPlot);
    ShowCurve(&stSpeedLimitCurve, true);

    setAssociatedWidget(&widget);
    UpdateCurveChoice();
}

void StSpaceDisplay::CreateObjectsTypeProperty(int32_t type)
{
    QString typeName = QString("Type %1").arg(type);
    if (objectsTypeTotalVector.contains(type)) {
        return;
    }

    objectsTypeTotalVector.append(type);
    if (objectsTypeProperty == nullptr) {
        objectsTypeProperty = new EnumProperty("Objects Type", typeName,
            "St Objects Type setting selection.", this, SLOT(UpdateObjectsTypeChoice()));
    }
    objectsTypeProperty->addOption(typeName, type);
}

void StSpaceDisplay::CreateObjectsTypeAlphaProperty(int32_t type, const QString &propertyKey,
    const QString &propertyValue)
{
    qreal alphaValue = propertyValue.toFloat();
    FloatProperty *objectsTypeAlphaPropertyPtr = new FloatProperty(propertyKey, alphaValue,
        "Objects type alpha setting.", objectsTypeProperty, SLOT(UpdateObjectsTypeColorAndAlpha()), this);
    objectsTypeAlphaPropertyPtr->setMin(COLOR_ALPHA_MIN);
    objectsTypeAlphaPropertyPtr->setMax(COLOR_ALPHA_MAX);
    SetObjectsTypeColorAlpha(type, alphaValue);
    objectsTypeAlphaProperty.insert(type, objectsTypeAlphaPropertyPtr);
}

void StSpaceDisplay::CreateObjectsTypeColorProperty(int32_t type, const QString &propertyKey,
    const QString &propertyValue)
{
    const int32_t colorChannelNum = 3;
    QStringList colorChannelValue = propertyValue.split("; ");
    if (colorChannelValue.size() < colorChannelNum) {
        ROS_ERROR("color channel is incomplete.");
        return;
    }

    int32_t red = colorChannelValue[0x0].toInt();
    int32_t green = colorChannelValue[0x01].toInt();
    int32_t blue = colorChannelValue[0x02].toInt();
    QColor color = QColor(red, green, blue);
    ColorProperty *objectsTypeColorPropertyPtr = new ColorProperty(propertyKey, color,
        "Objects type color setting.", objectsTypeProperty, SLOT(UpdateObjectsTypeColorAndAlpha()), this);

    SetObjectsTypeColor(type, red, green, blue);
    objectsTypeColorProperty.insert(type, objectsTypeColorPropertyPtr);
}

void StSpaceDisplay::CreateObjectsTypeTitleProperty(int32_t type, const QString &propertyKey,
    const QString &propertyValue)
{
    StringProperty *objectsTypeTitlePropertyPtr = new StringProperty(propertyKey, propertyValue,
        "Objects type title setting.", objectsTypeProperty, SLOT(UpdateObjectCurveTitle()), this);

    objectsTypeTitleProperty.insert(type, objectsTypeTitlePropertyPtr);
}

void StSpaceDisplay::CreateObjectProperty(const QString &propertyKey, const QString &propertyValue)
{
    QStringList valueList = propertyValue.split(' ');
    if (propertyKey == "Value" && valueList.size() == OBJECT_VALUE_KEY_VALUE_SIZE) {
        int32_t type = valueList[1].toInt();
        CreateObjectsTypeProperty(type);
        objectsTypeProperty->setString(propertyValue);
        return;
    }

    QStringList keyList = propertyKey.split(' ');
    if (keyList[0] != "Type" || keyList.size() != OBJECT_TYPE_KEY_KEY_SIZE) {
        return;
    }

    int32_t type = keyList[1].toInt();
    CreateObjectsTypeProperty(type);
    if (keyList.contains("Alpha")) {
        CreateObjectsTypeAlphaProperty(type, propertyKey, propertyValue);
        return;
    }

    if (keyList.contains("Color")) {
        CreateObjectsTypeColorProperty(type, propertyKey, propertyValue);
        return;
    }

    if (keyList.contains("Title")) {
        CreateObjectsTypeTitleProperty(type, propertyKey, propertyValue);
        return;
    }
}

void StSpaceDisplay::load(const rviz::Config &config)
{
    Display::load(config);
    rviz::Config configTmp = config.mapGetChild("Objects Type");
    rviz::Config::MapIterator mapIter = configTmp.mapIterator();
    while (mapIter.isValid()) {
        QString configKey = mapIter.currentKey();
        rviz::Config childConfig = configTmp.mapGetChild(configKey);
        QString value = childConfig.getValue().toString();
        CreateObjectProperty(configKey, value);
        mapIter.advance();
    }
    UpdateObjectsTypeChoice();
}

void StSpaceDisplay::UpdateObjectsTypeChoice()
{
    if (objectsTypeProperty == nullptr) {
        return;
    }

    int32_t type = objectsTypeProperty->getOptionInt();
    for (auto iter : objectsTypeTotalVector) {
        if (!objectsTypeTitleProperty.contains(iter) || objectsTypeTitleProperty[iter] == nullptr ||
            !objectsTypeColorProperty.contains(iter) || objectsTypeColorProperty[iter] == nullptr ||
            !objectsTypeAlphaProperty.contains(iter) || objectsTypeAlphaProperty[iter] == nullptr) {
                continue;
            }
        objectsTypeTitleProperty[iter]->setHidden(!(iter == type));
        objectsTypeColorProperty[iter]->setHidden(!(iter == type));
        objectsTypeAlphaProperty[iter]->setHidden(!(iter == type));
    }
}

void StSpaceDisplay::UpdateObjectsTypeColorAndAlpha()
{
    int32_t type = objectsTypeProperty->getOptionInt();

    if (!objectsTypeColorProperty.contains(type) || objectsTypeColorProperty[type] == nullptr ||
        !objectsTypeAlphaProperty.contains(type) || objectsTypeAlphaProperty[type] == nullptr) {
            return;
        }

    QColor objectColor = (objectsTypeColorProperty[type])->getColor();
    qreal alpha = (objectsTypeAlphaProperty[type])->getFloat();
    objectColor.setAlpha(alpha * 0xFF);
    SetObjectsTypeColor(type, objectColor);

    const QMap<QString, int32_t> idTypeMap = stSpaceData.GetObjectsIdTypeMap();

    for (auto iter = idTypeMap.begin(); iter != idTypeMap.end(); iter++) {
        int32_t typeTmp = iter.value();
        QString objectId = iter.key();

        if (typeTmp == type) {
            auto objectsCurveMap = stObjectsCurveMap.find(objectId);
            if (objectsCurveMap == stObjectsCurveMap.end() || stObjectsCurveMap[objectId] == nullptr) {
                continue;
            }
            stObjectsCurveMap[objectId]->setPen(objectColor, OBJECT_CURVE_PEN_WIDTH);
            stObjectsCurveMap[objectId]->setBrush(objectColor);
        }
    }

    ui.qwtPlot->replot();
}

QColor StSpaceDisplay::GetObjectsTypeColor(int32_t type)
{
    QHash<int32_t, QColor>::const_iterator iter = objectsTypeColor.find(type);
    if (iter != objectsTypeColor.end()) {
        return iter.value();
    }

    QColor color = QColor(GenerateRandom(COLOR_MIN, COLOR_MAX), GenerateRandom(COLOR_MIN, COLOR_MAX),
        GenerateRandom(COLOR_MIN, COLOR_MAX), COLOR_ALPHA_MAX);
    objectsTypeColor[type] = color;
    return color;
}

bool StSpaceDisplay::SetObjectsTypeColor(int32_t type, const QColor &color)
{
    QHash<int32_t, QColor>::const_iterator iter = objectsTypeColor.find(type);
    if (iter != objectsTypeColor.end()) {
        objectsTypeColor[type] = color;
        return true;
    }
    return false;
}

void StSpaceDisplay::SetObjectsTypeColor(int32_t type, int32_t red, int32_t green, int32_t blue)
{
    QHash<int32_t, QColor>::const_iterator iter = objectsTypeColor.find(type);

    QColor color;
    if (iter != objectsTypeColor.end()) {
        color = objectsTypeColor[type];
        color.setRed(red);
        color.setBlue(blue);
        color.setGreen(green);
        objectsTypeColor[type] = color;
        return;
    }
    color = QColor(red, green, blue, COLOR_ALPHA_MAX);
    objectsTypeColor[type] = color;
}

void StSpaceDisplay::SetObjectsTypeColorAlpha(int32_t type, int32_t alpha)
{
    QHash<int32_t, QColor>::const_iterator iter = objectsTypeColor.find(type);

    QColor color;
    if (iter != objectsTypeColor.end()) {
        color = objectsTypeColor[type];
        color.setAlpha(alpha);
        objectsTypeColor[type] = color;
        return;
    }
    color = QColor(GenerateRandom(COLOR_MIN, COLOR_MAX), GenerateRandom(COLOR_MIN, COLOR_MAX),
        GenerateRandom(COLOR_MIN, COLOR_MAX), alpha);
    objectsTypeColor[type] = color;
}

void StSpaceDisplay::SetObjectProperty(int32_t objectType)
{
    if (objectsTypeTotalVector.contains(objectType)) {
        return;
    }

    objectsTypeTotalVector.append(objectType);
    if (objectsTypeProperty == nullptr) {
        objectsTypeProperty = new EnumProperty("Objects Type", QString("Type %1").arg(objectType),
            "St Objects Type setting selection.", this, SLOT(UpdateObjectsTypeChoice()));
    }

    QString typeName = QString("Type %1").arg(objectType);
    objectsTypeProperty->addOption(typeName, objectType);

    StringProperty *objectsTypeTitlePropertyPtr =
        new StringProperty(QString("Type %1 Title").arg(objectType), QString("Type %1").arg(objectType),
        "Objects type title setting.", objectsTypeProperty, SLOT(UpdateObjectCurveTitle()), this);

    QColor color = GetObjectsTypeColor(objectType);
    ColorProperty *objectsTypeColorPropertyPtr =
        new ColorProperty(QString("Type %1 Color").arg(objectType), color, "Objects type color setting.",
        objectsTypeProperty, SLOT(UpdateObjectsTypeColorAndAlpha()), this);

    FloatProperty *objectsTypeAlphaPropertyPtr =
        new FloatProperty(QString("Type %1 Alpha").arg(objectType), color.alpha(),
        "Objects type alpha setting.", objectsTypeProperty, SLOT(UpdateObjectsTypeColorAndAlpha()), this);

    objectsTypeAlphaPropertyPtr->setMin(COLOR_ALPHA_MIN);
    objectsTypeAlphaPropertyPtr->setMax(COLOR_ALPHA_MAX);

    objectsTypeTitleProperty.insert(objectType, objectsTypeTitlePropertyPtr);
    objectsTypeColorProperty.insert(objectType, objectsTypeColorPropertyPtr);
    objectsTypeAlphaProperty.insert(objectType, objectsTypeAlphaPropertyPtr);
}

void StSpaceDisplay::ShowObjects(const QMap<QString, QPolygonF> &stObjectsMap)
{
    auto objectsTypeId = stSpaceData.GetObjectsTypeIdMap();

    for (auto iter = stObjectsCurveMap.begin(); iter != stObjectsCurveMap.end(); iter++) {
        QwtLegend *legend = qobject_cast<QwtLegend *>(ui.qwtPlot->legend());
        QList<QWidget *> legendWidgets = legend->legendWidgets(ui.qwtPlot->itemToInfo(iter->second.get()));
        for (int32_t i = 0; i < legendWidgets.size(); i++) {
            QwtLegendLabel *legendLabel = qobject_cast<QwtLegendLabel *>(legendWidgets.at(i));
            if (legendLabel != nullptr) {
                delete legendLabel;
                legendLabel = nullptr;
            }
        }
        iter->second->detach();
        iter->second->setSamples(QPolygonF());
    }
    stObjectsCurveMap.clear();

    QList<int32_t> listType = objectsTypeId.keys();

    for (auto objectType : listType) {
        QStringList listId = objectsTypeId.value(objectType);
        qSort(listId.begin(), listId.end());
        for (auto objectId : listId) {
            SetObjectProperty(objectType);
            if (!objectsTypeTitleProperty.contains(objectType) || objectsTypeTitleProperty[objectType] == nullptr) {
                continue;
            }
            QString objectName = QString("%1: %2").arg(objectsTypeTitleProperty[objectType]->getString()).arg(objectId);
            QColor color = GetObjectsTypeColor(objectType);
            unique_ptr<QwtPlotCurve> curve = make_unique<QwtPlotCurve>(objectName);
            curve->setTitle(objectName);
            color.setAlpha(objectsTypeAlphaProperty[objectType]->getFloat() * 0xFF);
            curve->setPen(color, OBJECT_CURVE_PEN_WIDTH);
            curve->setBrush(color);
            curve->setRenderHint(QwtPlotItem::RenderAntialiased, true);
            curve->setLegendAttribute(curve->LegendShowBrush);
            curve->attach(ui.qwtPlot);
            curve->setSamples(stObjectsMap.value(objectId));
            ShowCurve(curve.get(), stSpaceData.GetObjectVisible(objectId));
            stObjectsCurveMap.insert(std::make_pair(objectId, std::move(curve)));
        }
    }
}

void StSpaceDisplay::DrawObjects(const QMap<QString, QPolygonF> &stObjectsMap)
{
    ShowObjects(stObjectsMap);
    UpdateObjectsTypeChoice();
}

void StSpaceDisplay::onEnable()
{
    ChartDisplayClass::onEnable();
}

void StSpaceDisplay::ProcessMessage(const custom::StSpace::ConstPtr &message)
{
    if (message == nullptr) {
        setStatus(StatusProperty::Error, "Topic", "Topic Message is invalid!");
        return;
    }

    if (!stSpaceData.Add(message)) {
        return;
    }

    const QPolygonF firstStTrack = stSpaceData.GetFirstStTrack();
    const QPolygonF optimizedStTrack = stSpaceData.GetOptimizedStTrack();
    const QPolygonF stSpeedLimit = stSpaceData.GetStSpeedLimit();
    const QMap<QString, QPolygonF> stObjectsMap = stSpaceData.GetObjectsMap();

    firstStTrackCurve.setSamples(firstStTrack);
    optimizedStTrackCurve.setSamples(optimizedStTrack);
    stSpeedLimitCurve.setSamples(stSpeedLimit);

    DrawObjects(stObjectsMap);
    ui.qwtPlot->replot();
}

void StSpaceDisplay::ClearData()
{
    ShowCurve(&firstStTrackCurve, true);
    ShowCurve(&optimizedStTrackCurve, true);
    ShowCurve(&stSpeedLimitCurve, true);

    stSpaceData.Clear();
}

void StSpaceDisplay::ReplotEmptyData()
{
    QPolygonF emptyPolyon;
    firstStTrackCurve.setSamples(emptyPolyon);
    optimizedStTrackCurve.setSamples(emptyPolyon);
    stSpeedLimitCurve.setSamples(emptyPolyon);

    for (auto &iter : stObjectsCurveMap) {
        (iter.second)->detach();
        (iter.second)->setSamples(emptyPolyon);
    }
    ui.qwtPlot->replot();
}

void StSpaceDisplay::updateTopic()
{
    ClearData();
    ReplotEmptyData();
    sub.unsubscribe();
    stObjectsCurveMap.clear();
    reset();
    Subscribe();
}
}

// 声明此类是一个rviz的插件
#include <pluginlib/class_list_macros.h>
PLUGINLIB_EXPORT_CLASS(chart_plugin::StSpaceDisplay, rviz::Display)
